package com.dg.gaming.client.gui {
    import com.dg.gaming.api.common.GameModel;
    import com.dg.gaming.api.common.Player;
    import com.dg.gaming.api.common.PlayerList;
    import com.dg.gaming.api.common.Table;
    import com.dg.gaming.api.common.msg.ITransmittable;
    import com.dg.gaming.api.common.msg.MsgControllerObject;
    import com.dg.gaming.api.common.msg.MsgControllerProperty;
    import com.dg.gaming.api.common.msg.MsgNextPlayer;
    import com.dg.gaming.api.common.msg.MsgTableMessage;
    import com.dg.gaming.client.TableConnectionThread;

    import flash.display.DisplayObject;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;

    import nanoxml.XMLElement;

    public class GameController {
        /** Link to the game model. */
        protected var model:GameModel;

        /** Link to the DGComponent this is controlling . */
        protected var component:DisplayObject;

        /** Link to the client's connection thread. */
        protected var conn:TableConnectionThread;

        /** Table number which this controller is controlling. */
        protected var tableNum:String;

        /**
         * Reset method which is called every time a new game is created.  Each
         * sub class of this must have this method.  This method is called when the
         * game starts for the first time.
         */
        public function start():void {

        }


        /**
         * Default constructor class which doesn't set up the network connection.
         *
         * @param model           Model which the controller can update depending on
         *                        what the user input is.
         * @param DGComponent  The DGComponent which the controller is
         *                        listening on.
         */
        public function GameController(model:GameModel, component:DisplayObject) {
            this.model = model;
            this.component = component;
        }

        /**
         * Set up a network connection for this controller.
         *
         * @param conn      Link to the TableConnectionThread.
         */
        public function setConnection(conn:TableConnectionThread):void {
            this.conn = conn;
            this.tableNum = conn.getTableNum();
        }

        /**
         * Return the current player.
         *
         * @return  Current player's username.
         */
        public function getCurrentPlayer():Number {
            // if connection is not equal to null then there is access to the
            // current player etc.
            if (this.conn != null) {
                return getTable().getPlayerList().getCurrentPlayerId();
            }
            return -1;
        }

        /**
         * Return the player num (between 0 and the number of players).
         *
         * @return   Seat number of current player.
         */
        public function getCurrentPlayerSeatNum():int {
            // Check connection has been set
            if (this.conn != null) {
                var currentPlayer:Player = getTable().getPlayerList().getCurrentPlayer();
                if (currentPlayer != null) {
                    return currentPlayer.seatNum;
                }
            }
            return 0;
        }

        /**
         * Return the table number.
         *
         * @return   table number.
         */
        public function getTableNum():String {
            return this.tableNum;
        }

        /**
         * Convience method for returning a table.
         *
         * @return   Current table i.e. tableList.getTable (current table).
         */
        public function getTable():Table {
            return this.conn.getTable();
        }

        /**
         * Return the seat number of a user.
         *
         * @return   If -1 user is standing, otherwise seatNum &gt=0 and &lt number
         * of seats available.
         */
        public function getSeatNum():int {
            if (this.conn != null) {
                var playerList:PlayerList = getTable().getPlayerList();
                var player:Player = playerList.getPlayer(this.conn.getUserid());

                if (player != null) {
                    return player.seatNum;
                }
            }
            return Player.NOT_SEATED;
        }

        /**
         * Return the player name from a seat num.
         *
         * @param  seatNum  Seat number of the user.
         * @return   If -1 user is standing, otherwise seatNum &gt=0 and &lt number
         * of seats available.
         */
        public function getPlayer(seatNum:int):Number {
            if (this.conn != null) {
                var table:Table = this.conn.getTableList().getTable(this.tableNum);
                return table.getPlayerList().getPlayerBySeat(seatNum).playerId;
            }
            return -1;
        }

        /**
         * Return the seat number of a specified player.
         *
         * @param player	Name of the player.
         * @return          Number of specified players seat.
         */
        public function getSeatNumByPlayerId(player:Number):int {
            if (this.conn != null) {
                var table:Table = this.conn.getTableList().getTable(this.tableNum);
                return table.getPlayerList().getPlayer(player).seatNum;
            }
            return Player.NOT_SEATED;
        }

        /**
         * Return true/false if the game is started or not.
         *
         * NOTE: This will return true even if a player is not seated at a table.
         *
         * @return 	Returns true if a game is underway.
         */
        public function isGamePlaying():Boolean {
            if (this.conn != null) {
                var table:Table = this.conn.getTableList().getTable(this.tableNum);
                if (table != null) {
                    return table.isGamePlaying();
                }
            }
            return false;
        }

        /**
         * Returns true if game is playing AND player is also seated - useful
         * for non-turned based style games.
         *
         * @return
         */
        public function isPlayerPlaying():Boolean {
            if (this.conn != null) {
                var table:Table = this.conn.getTableList().getTable(this.tableNum);
                if (table != null) {
                    return table.isGamePlaying() && getSeatNum() != Player.NOT_SEATED;
                }
            }
            return false;
        }

        /**
         * Send a more complex object (any object which implements the ITransmittable
         * interface). The recieve (XMLElement element) method must be overwritten
         * to recieve it.
         *
         * @param object   A more complex piece of data encoded in XML.
         */
        public function sendIObject(object:ITransmittable):void {
            sendXMLObject(object.flatten());
        }

        /**
         * Send a more complex object (any object which implements the ITransmittable
         * interface).  The recieve (XMLElement element) method must be overwritten
         * to recieve it.
         *
         * @param message   A more complex piece of data encoded in XML.
         */
        public function sendXMLObject(message:XMLElement):void {
            // Create property communications object, add data and send to server
            var commObject:MsgControllerObject = new MsgControllerObject();
            commObject.setData(message);
            this.conn.send(commObject);
        }

        /**
         * Send a normal String valued property.
         *
         * @param key
         * @param value
         */
        public function sendProperty(key:String, value:String):void {
            // Create property communications object and send to server
            var commContProp:MsgControllerProperty = new MsgControllerProperty(MsgControllerProperty.TYPE_STRING, key, value);

            this.conn.send(commContProp);
        }

        /**
         * Send a single integer property.
         *
         * @param key
         * @param value
         */
        public function sendPropertyKeyValue(key:String, value:int):void {
            // Create property communications object and send to server
            var valueStr:String = value + "";
            var commContProp:MsgControllerProperty = new MsgControllerProperty(MsgControllerProperty.TYPE_INT, key, valueStr);
            this.conn.send(commContProp);
        }

        /**
         * Send a co-ordinate property (x and y integer values).
         *
         * @param key
         * @param x
         * @param y
         */
        public function sendPropertyKeyXY(key:String, x:int, y:int):void {
            // Create property communications object and send to server
            var commContProp:MsgControllerProperty = new MsgControllerProperty(MsgControllerProperty.TYPE_INT_TWO, key, x + " " + y);
            this.conn.send(commContProp);
        }

        /**
         * Adapter method for receiving property as a key and value from another client.
         *
         * @param key    Key of property to be read from server.
         * @param value  String value of property to be read from server.
         */
        public function receivePropertyKeyValue(key:String, value:String):void {
            // over written in sub class
        }

        /**
         * Recieve property as a String key and integer value.
         *
         * @param key    Key of property to be read from server.
         * @param value  Integer value of property to be read from server.
         */
        public function receivePropertyKeyValueInt(key:String, value:int):void {
            // over written in sub class
        }

        /**
         * Recieve property from another client as a co-ordinate (x and y integer).
         *
         * @param key    Key of property to be read from server.
         * @param x      Integer x co-ordinate value property to be read from server.
         * @param y      Integer y co-ordinate value property to be read from server.
         */
        public function receivePropertyKeyXY(key:String, x:int, y:int):void {
            // over written in sub class
        }

        /**
         * Recieve object as XML from a client or server (use this when more advanced
         * data is required from the server or a client).
         *
         * @param message   Receive a more complex XML object from a server.
         */
        public function receiveObjectXML(message:XMLElement):void {
        }

        /**
         * Return new if it is this players turn.
         *
         * @return  True if this current players turn.
         */
        public function isThisPlayersTurn():Boolean {
            if (PlayerList.NO_PLAYER == getCurrentPlayer()) {
                return false;
            } else {
                return (getSeatNum() == getCurrentPlayerSeatNum());
            }
        }


        /**
         * Method which tells the server that it is next players turn.
         */
        public function nextPlayer(childMsg:MsgTableMessage = null):void {
            // Create communcations table action of type "Next Player".
            var table:Table = getTable();

            var nextPlayer:MsgNextPlayer = new MsgNextPlayer(table.getTableNum(), getCurrentPlayer(), childMsg);

            // Fix for bug 1176951 - "Possibility of consequent moves"
            conn.getTable().getPlayerList().setCurrentPlayer(PlayerList.NO_PLAYER);

            // send to server from client
            this.conn.send(nextPlayer);
        }

        //==========================================================================
        // Empty implementations of "MouseListener"
        //==========================================================================

        /**
         * Invoked when the mouse enters a DGComponent.
         *
         * @param e
         */
        public function mouseEntered(e:MouseEvent):void {
        }

        /**
         * Invoked when the mouse exits a DGComponent.
         *
         * @param e
         */
        public function mouseExited(e:MouseEvent):void {
        }

        /**
         * Invoked when the mouse has been clicked on a DGComponent.
         *
         * @param e
         */
        public function mouseClicked(e:MouseEvent):void {
        }

        /**
         * Invoked when the mouse has been double clicked on a DGComponent.
         *
         * @param e
         */
        public function mouseDBClicked(e:MouseEvent):void {
        }

        /**
         * Invoked when a mouse button has been released on a DGComponent.
         *
         * @param e
         */
        public function mouseReleased(e:MouseEvent):void {
        }

        /**
         * Invoked when a mouse button has been pressed on a DGComponent.
         *
         * @param e
         */
        public function mousePressed(e:MouseEvent):void {
        }

        /**
         * Invoked when a mouse button has been wheeled on a DGComponent.
         *
         * @param e
         */
        public function mouseWheel(e:MouseEvent):void {
        }

        //==========================================================================
        // Empty implementations of "MouseMotionListener"
        //==========================================================================

        /**
         * Invoked when the mouse button has been moved on a DGComponent
         * (with no buttons no down).
         *
         * @param e
         */
        public function mouseMoved(e:MouseEvent):void {
        }

        /**
         * Invoked when a mouse button is pressed on a DGComponent and
         * then dragged.
         *
         * @param e
         */
        public function mouseDragged(e:MouseEvent):void {
        }

        //==========================================================================
        // Empty implementations of "KeyListener"
        //==========================================================================

        /**
         * Invoked when a key has been pressed.
         *
         * @param e
         */
        public function keyPressed(e:KeyboardEvent):void {
        }

        /**
         * Invoked when a key has been released.
         *
         * @param e
         */
        public function keyReleased(e:KeyboardEvent):void {
        }
//
//        /**
//         * Invoked when a key has been typed.
//         *
//         * @param e
//         */
//        public function keyTyped(e:KeyboardEvent):void {
//        }

    }
}